/*FreeMind - A Program for creating and viewing Mindmaps
 *Copyright (C) 2000-2001  Joerg Mueller <joergmueller@bigfoot.com>
 *See COPYING for Details
 *
 *This program is free software; you can redistribute it and/or
 *modify it under the terms of the GNU General Public License
 *as published by the Free Software Foundation; either version 2
 *of the License, or (at your option) any later version.
 *
 *This program is distributed in the hope that it will be useful,
 *but WITHOUT ANY WARRANTY; without even the implied warranty of
 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *GNU General Public License for more details.
 *
 *You should have received a copy of the GNU General Public License
 *along with this program; if not, write to the Free Software
 *Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package freemind.modes.mindmapmode.listeners;

import java.awt.Component;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.util.List;
import java.util.ListIterator;

import javax.swing.JOptionPane;

import freemind.controller.MindMapNodesSelection;
import freemind.modes.MindMapNode;
import freemind.modes.mindmapmode.MindMapController;
import freemind.modes.mindmapmode.MindMapMapModel;
import freemind.modes.mindmapmode.MindMapNodeModel;
import freemind.view.mindmapview.MainView;
import freemind.view.mindmapview.NodeView;

// import ublic class MindMapNodesSelection implements Transferable,
// ClipboardOwner {
// public static DataFlavor fileListFlavor = null;

public class MindMapNodeDropListener implements DropTargetListener {

    private final MindMapController mMindMapController;

    public MindMapNodeDropListener(MindMapController controller) {
        mMindMapController = controller;
    }

    // See DropTargetListener for the meaning.
    private boolean isDragAcceptable(DropTargetDragEvent ev) {
        if (ev.isDataFlavorSupported(DataFlavor.stringFlavor)) {
            return true;
        }
        if (ev.isDataFlavorSupported(MindMapNodesSelection.fileListFlavor)) {
            return true;
        }
        return false;
    }

    private boolean isDropAcceptable(DropTargetDropEvent event) {
        MindMapNode node = ((MainView) event.getDropTargetContext()
                .getComponent()).getNodeView().getModel();
        MindMapNode selected = mMindMapController.getSelected();
        if(!node.isWriteable())
        	return false;
        return ((node != selected) && !node.isDescendantOf(selected));
        // I think (node!=selected) is a hack for windows
    }

    public void drop(DropTargetDropEvent dtde) {
        try {
            int dropAction = dtde.getDropAction();
            Transferable t = dtde.getTransferable();

            final MainView mainView = (MainView)dtde.getDropTargetContext()                    .getComponent();
            NodeView targetNodeView =  mainView.getNodeView();
            MindMapNode targetNode = targetNodeView.getModel();
            MindMapNodeModel targetNodeModel = (MindMapNodeModel) targetNode;

            // Intra application DnD

            // For some reason, getting sourceAction is only possible for local
            // transfer. When I try to remove clause dtde.isLocalTransfer, I get
            // an answer
            // like "no drop current". One hypothesis is that with nonlocal
            // transfers, I
            // have to accept drop action before I can get transfer data.
            // However, this is
            // not what I want in this particular situation. A part of the
            // problem lies in
            // the hackery of sending source action using data flavour too.
            if (dtde.isLocalTransfer()
                    && t
                            .isDataFlavorSupported(MindMapNodesSelection.dropActionFlavor)) {
                String sourceAction = (String) t
                        .getTransferData(MindMapNodesSelection.dropActionFlavor);
                if (sourceAction.equals("LINK")) {
                    dropAction = DnDConstants.ACTION_LINK;
                }
                if (sourceAction.equals("COPY")) {
                    dropAction = DnDConstants.ACTION_COPY;
                }
            }

            mainView.setDraggedOver(NodeView.DRAGGED_OVER_NO);
            mainView.repaint();

            if (dtde.isLocalTransfer()
                    && (dropAction == DnDConstants.ACTION_MOVE)
                    && !isDropAcceptable(dtde)) {
                dtde.rejectDrop();
                return;
            }

            dtde.acceptDrop(dtde.getDropAction());

            if (!dtde.isLocalTransfer()) {
                // if
                // (dtde.isDataFlavorSupported(MindMapNodesSelection.fileListFlavor))
                // {
                // System.err.println("filelist");
                mMindMapController.paste(t, targetNode, 
                        mainView.dropAsSibling(dtde.getLocation().getX()),
                        mainView.dropPosition(dtde.getLocation().getX()));
                dtde.dropComplete(true);
                return;
            }

            // This all is specific to MindMap model. Needs rewrite to work for
            // other modes.
            // We ignore data transfer in dtde. We take selected nodes as drag
            // sources.
            // <problem>
            // The behaviour is not so fine, when some of selected nodes is an
            // ancestor of other selected nodes.
            // Ideally, we would first unselect all nodes, which have an
            // ancestor among selected nodes.
            // I don't have time/lust to do this. This is just a minor problem.
            // </problem>

            // By transferable object we only transfer source action. This will
            // be a problem, when we want
            // to implement extra application dnd or dnd between different Java
            // Virtual Machines.

            if (dropAction == DnDConstants.ACTION_LINK) {
                // ACTION_LINK means for us change the color, style and font.

                // This is not very clean. This all should probably be
                // implemented on the mindMapMapModel level. On the other
                // hand, one would have to downcast to MindMapMapModel anyway.

                // MindMapNode selectedNode =
                // c.getView().getSelected().getModel();
                MindMapMapModel mindMapMapModel = (MindMapMapModel) mMindMapController
                        .getModel();

                // link feature continues here. fc, 01.11.2003:
                // if there are more than 4 nodes, then ask the user:
                int yesorno = JOptionPane.YES_OPTION;
                if (mMindMapController.getView().getSelecteds().size() >= 5) {
                    yesorno = JOptionPane.showConfirmDialog(mMindMapController
                            .getFrame().getContentPane(), mMindMapController
                            .getText("lots_of_links_warning"), Integer
                            .toString(mMindMapController.getView()
                                    .getSelecteds().size())
                            + " links to the same node",
                            JOptionPane.YES_NO_OPTION);
                }
                if (yesorno == JOptionPane.YES_OPTION) {
                    for (ListIterator it = mMindMapController.getView()
                            .getSelecteds().listIterator(); it.hasNext();) {
                        MindMapNodeModel selectedNodeModel = (MindMapNodeModel) ((NodeView) it
                                .next()).getModel();
                        // mindMapMapModel.setNodeColor(selectedNodeModel,targetNode.getColor());
                        // mindMapMapModel.setNodeFont(selectedNodeModel,targetNode.getFont());
                        mMindMapController.addLink(selectedNodeModel,
                                targetNodeModel);
                    }
                }
            } else {
            	if(!targetNode.isWriteable()){
            		String message = mMindMapController
							.getText("node_is_write_protected");
					JOptionPane.showMessageDialog(mMindMapController.getFrame()
							.getContentPane(), message, "Freemind",
							JOptionPane.ERROR_MESSAGE);
            		return;
            	}
                Transferable trans = null;
                // if move, verify, that the target is not a son of the sources.
                List selecteds = mMindMapController.getSelecteds();
                if (DnDConstants.ACTION_MOVE == dropAction) {
                    MindMapNode actualNode = targetNode;
                    do {
                        if (selecteds.contains(actualNode)) {
                            String message = mMindMapController
                                    .getText("cannot_move_to_child");
                            JOptionPane.showMessageDialog(mMindMapController
                                    .getFrame().getContentPane(), message,
                                    "Freemind", JOptionPane.WARNING_MESSAGE);
                            dtde.dropComplete(true);
                            return;
                        }
                        actualNode = (actualNode.isRoot()) ? null : actualNode
                                .getParentNode();
                    } while (actualNode != null);
                    trans = mMindMapController.cut();
                } else {
                	trans = mMindMapController.copy();
                }

                mMindMapController.getView().selectAsTheOnlyOneSelected(
                        targetNodeView);
                boolean result = mMindMapController.paste(trans, targetNode, mainView.dropAsSibling(dtde.getLocation().getX()),
                        mainView.dropPosition(
                                dtde.getLocation().getX()));
                if (!result && DnDConstants.ACTION_MOVE == dropAction) {
                	// an error occured. how to react?
                	
                }
            }
        } catch (Exception e) {
            System.err.println("Drop exception:" + e);
            freemind.main.Resources.getInstance().logException(e);
            dtde.dropComplete(false);
            return;
        }
        dtde.dropComplete(true);
    }

    /**
     * The method is called when the cursor carrying the dragged item enteres
     * the area of the node. The name "dragEnter" seems to be confusing to me.
     * 
     * I think the difference between dragAcceptable and dropAcceptable is that
     * in dragAcceptable, you tell if the type of the thing being dragged is OK,
     * where in dropAcceptable, you tell if your really willing to accept the
     * item.
     */
    public void dragEnter(DropTargetDragEvent dtde) {
        // TODO: Accepting the action ACTION_MOVE is false, because we cannot
        // know if the action is really ACTION_MOVE.
        if (isDragAcceptable(dtde)) {
            dtde.acceptDrag(DnDConstants.ACTION_MOVE);
        } else {
            dtde.rejectDrag();
        }
    }

    public void dragOver(DropTargetDragEvent e) {
        MainView draggedNode = (MainView) e.getDropTargetContext()
                .getComponent();
        int oldDraggedOver = draggedNode.getDraggedOver();
        // let the node decide, which dragged over type it is:
        draggedNode.setDraggedOver(e.getLocation());
        int newDraggedOver = draggedNode.getDraggedOver();
        boolean repaint = newDraggedOver != oldDraggedOver;
        if (repaint) {
            draggedNode.repaint();
        }
    }

    public void dragExit(DropTargetEvent e) {
        MainView draggedNode = (MainView) e.getDropTargetContext()
                .getComponent();
        draggedNode.setDraggedOver(NodeView.DRAGGED_OVER_NO);
        draggedNode.repaint();
    }

    public void dragScroll(DropTargetDragEvent e) {
    }

    public void dropActionChanged(DropTargetDragEvent e) {
    }
}
